package com.fury.managers;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;

import com.fury.framework.Task;

public class TaskManager {
	private static final TaskManager INSTANCE = new TaskManager();

	public static TaskManager getInstance() {
		return INSTANCE;
	}

	// private final BlockingQueue<Task> queue;
	private final List<List<Task>> buffer;
	private final TaskWorker[] workers;
	private final CyclicBarrier startBarrier;
	private final CyclicBarrier endBarrier;
	private final int cores;
	private final AtomicInteger atomicSystems;
	private final AtomicInteger atomicDrains;

	private TaskManager() {
		buffer = new ArrayList<List<Task>>();

		atomicSystems = new AtomicInteger(0);
		atomicDrains = new AtomicInteger(0);

		// TODO make configurable
		cores = Runtime.getRuntime().availableProcessors();

		startBarrier = new CyclicBarrier(cores + 1);
		endBarrier = new CyclicBarrier(cores + 1);

		workers = new TaskWorker[cores];

		for (int i = 0; cores > i; i++) {
			workers[i] = new TaskWorker();
			new Thread(workers[i]).start();
		}
	}

	public List<List<Task>> getBuffer() {
		return buffer;
	}

	public void processTasks() {
		atomicDrains.set(0);
		atomicSystems.set(0);

		try {
			startBarrier.await(); // Signal workers to start working, unblocks.
			startBarrier.reset();

			endBarrier.await(); // Wait for all workers to end.
			endBarrier.reset();
		} catch (InterruptedException e) {
			e.printStackTrace(); // TODO handle more gracefully
		} catch (BrokenBarrierException e) {
			e.printStackTrace(); // TODO handle more gracefully
		}
	}

	public class TaskWorker implements Runnable {
		// TODO make configurable.
		private static final int DRAINCOUNT = 100;

		@Override
		public void run() {
			// Run always.
			while (true) {
				try {
					// Block until all are ready.
					startBarrier.await();
				} catch (InterruptedException e) {
					e.printStackTrace(); // TODO handle more gracefully
				} catch (BrokenBarrierException e) {
					e.printStackTrace(); // TODO handle more gracefully
				}

				// Drain from the queue.
				while (true) {
					int drains = atomicDrains.incrementAndGet() - 1;
					int from = drains * DRAINCOUNT;

					if (from > buffer.size())
						break;

					int to = (drains + 1) * DRAINCOUNT;
					to = to > buffer.size() ? buffer.size() : to;
					List<Task> tasks = buffer.get(getSystemIndex()).subList(from, to);
					for (Task task : tasks) {
						task.execute();
					}
				}

				try {
					// This worker is done, block until all are done.
					endBarrier.await();
				} catch (InterruptedException e) {
					e.printStackTrace(); // TODO handle more gracefully
				} catch (BrokenBarrierException e) {
					e.printStackTrace(); // TODO handle more gracefully
				}

			}
		}

		private synchronized int getSystemIndex() {
			return 0;
		}

	}

}
